import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import * as CryptoJS from 'crypto-js';
import * as mimeTypes from 'mime-types';
import { CHECKSUM_ALGORITHM, ENCRYPTION } from './constants';
import { MDZErrorType, MDZError } from './types';

/**
 * 生成随机ID
 * @returns 随机生成的UUID
 */
export function generateId(): string {
  return crypto.randomUUID();
}

/**
 * 计算文件或数据的校验和
 * @param data 文件路径或Buffer数据
 * @returns 校验和字符串
 */
export function calculateChecksum(data: string | Buffer): Promise<string> {
  return new Promise((resolve, reject) => {
    try {
      let hash: crypto.Hash;
      
      if (typeof data === 'string' && fs.existsSync(data)) {
        // 如果是文件路径，创建流式处理
        hash = crypto.createHash(CHECKSUM_ALGORITHM);
        const stream = fs.createReadStream(data);
        
        stream.on('data', (chunk) => {
          hash.update(chunk);
        });
        
        stream.on('end', () => {
          resolve(hash.digest('hex'));
        });
        
        stream.on('error', (err) => {
          reject(new MDZError(`Failed to calculate checksum: ${err.message}`, MDZErrorType.IO_ERROR));
        });
      } else {
        // 如果是字符串或Buffer，直接计算
        const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
        hash = crypto.createHash(CHECKSUM_ALGORITHM);
        hash.update(buffer);
        resolve(hash.digest('hex'));
      }
    } catch (err: any) {
      reject(new MDZError(`Failed to calculate checksum: ${err.message}`, MDZErrorType.UNKNOWN_ERROR));
    }
  });
}

/**
 * 验证文件或数据的校验和
 * @param data 文件路径或Buffer数据
 * @param expectedChecksum 期望的校验和
 * @returns 是否匹配
 */
export async function verifyChecksum(data: string | Buffer, expectedChecksum: string): Promise<boolean> {
  try {
    const actualChecksum = await calculateChecksum(data);
    return actualChecksum === expectedChecksum;
  } catch (err) {
    throw err;
  }
}

/**
 * 加密数据
 * @param data 要加密的数据
 * @param password 密码
 * @returns 加密后的数据
 */
export function encryptData(data: string | Buffer, password: string): string {
  try {
    const dataStr = Buffer.isBuffer(data) ? data.toString('utf-8') : data;
    
    // 生成随机盐和IV
    const salt = crypto.randomBytes(16);
    const iv = crypto.randomBytes(16);
    
    // 从密码派生密钥
    const key = crypto.pbkdf2Sync(
      password,
      salt,
      1000,  // 降低迭代次数，提高性能
      32,    // 256位密钥
      'sha256'
    );
    
    // 创建加密器
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    
    // 加密数据
    let encrypted = cipher.update(dataStr, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    // 组合所有部分，使用清晰的分隔符
    const result = {
      algorithm: 'aes-256-cbc',
      salt: salt.toString('hex'),
      iv: iv.toString('hex'),
      data: encrypted
    };
    
    return JSON.stringify(result);
  } catch (err: any) {
    throw new MDZError(`Encryption failed: ${err.message}`, MDZErrorType.ENCRYPTION_ERROR);
  }
}

/**
 * 解密数据
 * @param encryptedData 加密数据
 * @param password 密码
 * @returns 解密后的数据
 */
export function decryptData(encryptedData: string, password: string): string {
  try {
    // 尝试解析JSON结构
    let encData;
    try {
      encData = JSON.parse(encryptedData);
    } catch (e) {
      // 如果无法解析为JSON，尝试使用旧格式解析
      console.warn('Unable to parse as JSON, trying legacy format...');
      const saltSize = 32; // 16字节盐的十六进制表示长度
      const ivSize = 32;   // 16字节IV的十六进制表示长度
      
      if (encryptedData.length < saltSize + ivSize) {
        throw new MDZError('Invalid encrypted data format or insufficient length', MDZErrorType.ENCRYPTION_ERROR);
      }
      
      const salt = Buffer.from(encryptedData.substr(0, saltSize), 'hex');
      const iv = Buffer.from(encryptedData.substr(saltSize, ivSize), 'hex');
      const encrypted = encryptedData.substring(saltSize + ivSize);
      
      // 使用旧格式继续解密流程
      return decryptWithComponents(encrypted, salt, iv, password);
    }
    
    // 确保所有必要字段都存在
    if (!encData.salt || !encData.iv || !encData.data) {
      throw new MDZError('Missing required fields in encrypted data', MDZErrorType.ENCRYPTION_ERROR);
    }
    
    // 提取各部分
    const salt = Buffer.from(encData.salt, 'hex');
    const iv = Buffer.from(encData.iv, 'hex');
    const encrypted = encData.data;
    
    return decryptWithComponents(encrypted, salt, iv, password);
  } catch (err: any) {
    if (err instanceof MDZError) {
      throw err;
    }
    throw new MDZError(`Decryption failed: ${err.message}`, MDZErrorType.ENCRYPTION_ERROR);
  }
}

/**
 * 使用给定的组件进行解密
 * @param encrypted 加密文本
 * @param salt 盐
 * @param iv 初始化向量
 * @param password 密码
 * @returns 解密后的文本
 */
function decryptWithComponents(encrypted: string, salt: Buffer, iv: Buffer, password: string): string {
  // 从密码派生密钥
  const key = crypto.pbkdf2Sync(
    password,
    salt,
    1000,  // 与加密时相同
    32,    // 256位密钥
    'sha256'
  );
  
  // 创建解密器
  const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  
  try {
    // 解密数据
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
  } catch (err: any) {
    throw new MDZError(`Decryption failed: ${err.message}`, MDZErrorType.ENCRYPTION_ERROR);
  }
}

/**
 * 获取文件的MIME类型
 * @param filePath 文件路径
 * @returns MIME类型
 */
export function getMimeType(filePath: string): string {
  const mimeType = mimeTypes.lookup(filePath);
  return mimeType || 'application/octet-stream';
}

/**
 * 验证路径是否安全（防止目录遍历攻击）
 * @param basePath 基础路径
 * @param targetPath 目标路径
 * @returns 是否安全
 */
export function isPathSafe(basePath: string, targetPath: string): boolean {
  const normalizedBasePath = path.normalize(basePath);
  const resolvedPath = path.resolve(normalizedBasePath, targetPath);
  return resolvedPath.startsWith(normalizedBasePath);
}

/**
 * 将相对路径转换为绝对路径并验证安全性
 * @param basePath 基础路径
 * @param relativePath 相对路径
 * @returns 安全的绝对路径
 */
export function safeResolve(basePath: string, relativePath: string): string {
  const absolutePath = path.resolve(basePath, relativePath);
  if (!isPathSafe(basePath, relativePath)) {
    throw new MDZError('Unsafe path access detected', MDZErrorType.INVALID_FORMAT);
  }
  return absolutePath;
} 